package org.yourscrum.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.yourscrum.domain.PlanType;
import static org.yourscrum.domain.PlanType.AFTER;
import static org.yourscrum.domain.PlanType.BEFORE;
import org.yourscrum.domain.Story;
import org.yourscrum.domain.StoryRepository;
import org.yourscrum.domain.Task;

import java.util.List;

/**
 * @author Jettro Coenradie
 */
@Service("storyService")
public class BacklogServiceImpl implements BacklogService {
    private StoryRepository storyRepository;

    @Autowired
    public BacklogServiceImpl(StoryRepository storyRepository) {
        this.storyRepository = storyRepository;
    }

    @Transactional(readOnly = true)
    public List<Story> listAllStories() {
        return storyRepository.listAllStories();
    }

    @Transactional
    public Story obtainStoryById(Long id) {
        return storyRepository.obtainStoryById(id);
    }

    @Transactional
    public Story createStory(String name, String description) {
        Story story = new Story(name);
        story.changeDescription(description);
        Story leastImportantStory = storyRepository.findLeastImportantStory();
        storyRepository.addStory(story);
        if (leastImportantStory != null) {
            leastImportantStory.planBefore(story);
        }
        return story;
    }

    @Transactional
    public Story changeStory(long id, String name, String description) {
        Story story = storyRepository.obtainStoryById(id);
        story.changeDescription(description);
        story.changeName(name);
        storyRepository.updateStory(story);
        return story;
    }

    @Transactional
    public Story addTaskToStory(long storyId, String description, int estimatedHours) {
        Story story = storyRepository.obtainStoryById(storyId);
        Task task = story.addNewTask(description);
        task.changeEstimatedHours(estimatedHours);
        storyRepository.updateStory(story);
        return story;
    }

    @Transactional
    public void plan(Long storyId, PlanType planType, Long otherStoryId) {
        Assert.notNull(planType, "A PlanType must be provided when planning a story");
        Assert.notNull(storyId, "The moved story cannot be null");
        Story story = storyRepository.obtainStoryById(storyId);
        Story otherStory = null;
        if (otherStoryId != null) {
            otherStory = storyRepository.obtainStoryById(otherStoryId);
        }
        if (story == null || (otherStoryId != null && otherStory == null)) {
            throw new BacklogException("The id of the story to plan or the other story is cannot be found");
        }

        if (planType == BEFORE) {
            Story nextStory = story.nextStory();
            Story previousStory = storyRepository.findPreviousStory(story.getId());
            Story previousStoryOther = null;
            if (otherStoryId != null) {
                previousStoryOther = storyRepository.findPreviousStory(otherStory.getId());
            }
            if (otherStory != null) {
                story.planBefore(otherStory);
            } else {
                storyRepository.findLeastImportantStory().planBefore(story);
                story.planAtEnd();
            }
            if (previousStoryOther != null) {
                previousStoryOther.planBefore(story);
            }
            if (previousStory != null) {
                if (nextStory != null) {
                    previousStory.planBefore(nextStory);
                } else {
                    previousStory.planAtEnd();
                }
            }
        } else if (planType == AFTER) {
            Story nextStory = otherStory.nextStory();
            if (nextStory == null) {
                story.planAtEnd();
            } else {
                story.planBefore(nextStory);
            }
            otherStory.planBefore(story);
        } else {
            throw new BacklogException("Unsupported plantype is provided : " + planType);
        }
    }

}
